package com.zodiac.alioth.web.controller;


import com.alibaba.fastjson.JSON;
import com.zodiac.alioth.common.api.*;
import com.zodiac.alioth.common.enums.ApiRespCodeEnum;
import com.zodiac.alioth.common.model.vo.ApiResponse;
import com.zodiac.alioth.web.exception.AppException;
import lombok.extern.slf4j.Slf4j;
import org.springframework.util.StreamUtils;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.net.URLDecoder;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.util.Collections;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.Map;

/**
 * Created by well on 2018/5/13.
 */
@Slf4j
@RestController
public class ApiController {

    private static final String UPLOAD_CONTENT_TYPE = "multipart/form-data";

    @Resource
    private ApiServiceFactory apiServiceFactory;

    /**
     * 支持将url:/rest/api/v1/user.account 转成apiName=user.account  apiVersion=v1
     * 支持将url:/rest/api/v1/user.account.test  转成apiName=user.account.test  apiVersion=v1
     *
     * @param request    HttpServletRequest
     * @param apiName    apiName
     * @param apiVersion apiVersion
     * @return ApiResponse
     */
//    @CsrfCheck
    @RequestMapping("/rest/api/{apiVersion:.*}/{apiName:.*}")
    public ApiResponse<?> apiWithRegexPath(HttpServletRequest request, HttpServletResponse response, @PathVariable(value = "apiName") String apiName, @PathVariable(value = "apiVersion") String apiVersion) {
        request.setAttribute("apiName", apiName);
        request.setAttribute("apiVersion", apiVersion);
        if (log.isInfoEnabled()) {
            log.info("apiWithRegexPath:apiName:" + apiName + ",apiVersion:" + apiVersion);
        }
        return api(request, response);
    }


    /**
     * 支持将url:/rest/api?apiName=user.account&apiVersion=v1  转成对应的api:apiName=user.account  apiVersion=v1
     * 支持将url:/rest/api,header中设置apiName=user.account apiVersion=v1  转成对应的api:apiName=user.account  apiVersion=v1
     *
     * @param request HttpServletRequest
     * @return ApiResponse
     */
    public ApiResponse<?> api(HttpServletRequest request, HttpServletResponse response) {
        String contentType = request.getContentType();
        if (UPLOAD_CONTENT_TYPE.equals(contentType)) {
            //文件上传的，目前无法处理
            return ApiResponse.getResult(ApiRespCodeEnum.UPDATE_NOT_ALLOW);
        }
        ApiRequestKey apiRequestKey = getApiRequestKey(request);
        if (!StringUtils.hasText(apiRequestKey.getApiName()) || !StringUtils.hasText(apiRequestKey.getApiVersion())) {
            return ApiResponse.getResult(ApiRespCodeEnum.METHOD_NOT_ALLOW);
        }
        String requestBody = getRequestBody(request);
        if (StringUtils.isEmpty(requestBody)) {
            requestBody = (String) request.getAttribute("plainRequestBody");
        }
        Map<String, Object> parameterMap = getAllParameter(request);
        ApiRequest apiRequest = new ApiRequest();
        apiRequest.setBody(requestBody);
        ApiHandler apiHandler = apiServiceFactory.getService(apiRequestKey);
        if (apiHandler == null) {
            log.error("无法找到对应的处理方法:" + apiRequestKey);
            return ApiResponse.getResult(ApiRespCodeEnum.METHOD_NOT_ALLOW);
        }
        apiRequest.setApiHandler(apiHandler);
        apiRequest.setApiRequestKey(apiRequestKey);
        apiRequest.setRequestParams(parameterMap);
        apiRequest.setHttpServletRequest(request);
        apiRequest.setHttpServletResponse(response);
        ApiMethodInvoker apiMethodInvoker = new ApiMethodInvoker();
        try {
            ApiResponse<?> apiResponse = apiMethodInvoker.invoke(apiRequest, apiHandler);
            if (log.isInfoEnabled()) {
                log.info("【api request】:" + JSON.toJSONString(apiRequest) + "【api response】:" + JSON.toJSONString(apiResponse));
            }
            return apiResponse;
        } catch (AppException e) {
            log.error("程序异常 AppException", e);
            throw e;
        } catch (Exception e) {
            log.error("程序异常 Exception", e);
            return ApiResponse.getFailureResult(ApiRespCodeEnum.ERROR.getCode(), e.getMessage());
        }
    }

    private ApiRequestKey getApiRequestKey(HttpServletRequest request) {
        ApiRequestKey apiRequestKey = new ApiRequestKey();
        Object apiNameAttr = request.getAttribute("apiName");
        Object apiVersionAttr = request.getAttribute("apiVersion");
        if (apiNameAttr != null && apiVersionAttr != null) {
            apiRequestKey.setApiName(String.valueOf(apiNameAttr));
            apiRequestKey.setApiVersion(String.valueOf(apiVersionAttr));
            return apiRequestKey;
        }
        String apiName = request.getHeader("apiName");
        String apiVersion = request.getHeader("apiVersion");
        if (!StringUtils.hasText(apiName)) {
            apiName = request.getParameter("apiName");
        }
        if (!StringUtils.hasText(apiVersion)) {
            apiVersion = request.getParameter("apiVersion");
        }
        apiRequestKey.setApiName(apiName);
        apiRequestKey.setApiVersion(apiVersion);
        return apiRequestKey;
    }

    private String getRequestBody(HttpServletRequest request) {
        try {
            return StreamUtils.copyToString(request.getInputStream(), StandardCharsets.UTF_8);
        } catch (Exception e) {
            log.error(this.getClass() + "", e);
        }
        return null;
    }


    private Map<String, Object> getAllParameter(HttpServletRequest httpServletRequest) {
        if (httpServletRequest == null) {
            return Collections.emptyMap();
        }
        Map<String, String[]> parameterMap = httpServletRequest.getParameterMap();
        Map<String, Object> param = new HashMap<>();
        for (Map.Entry<String, String[]> next : parameterMap.entrySet()) {
            String key = next.getKey();
            String[] value = next.getValue();
            if (value == null) {
                continue;
            }
            if (value.length == 1) {
                param.put(key, value[0]);
            } else {
                param.put(key, value);
            }
        }
        return param;
    }

    private Map<String, String> parseForm(String formData, Charset charset) throws IOException {
        Map<String, String> formMap = new LinkedHashMap<>();
        if (formData == null) {
            return formMap;
        }

        String[] pairs = StringUtils.tokenizeToStringArray(formData, "&");
        for (String pair : pairs) {
            int idx = pair.indexOf('=');
            if (idx == -1) {
                formMap.put(URLDecoder.decode(pair, charset.name()), null);
            } else {
                String name = URLDecoder.decode(pair.substring(0, idx), charset.name());
                String value = URLDecoder.decode(pair.substring(idx + 1), charset.name());
                formMap.put(name, value);
            }
        }
        return formMap;
    }
}
